package wiModel;

import hello.jni.HelloWorld;
import java.util.ArrayList;
import wiBase.WiException;
import wiBase.WiObject;
import wiBase.baseInterface.IFilterDataCollector;
import wiBase.baseObject.Event;
import wiBase.baseObject.Node;
import wiBase.baseObject.Packet;
/**
 * manager of WiModel.
 * contain data from trace file.
 */
public class WiModelManager {
    private static final boolean FLAG_DEBUG=true;
    
    private static WiModelManager _mng;
    private String _fileNameConfig = "";
    private String _fileNameTrace = "./res/trace/trace.xml";
    private double width = 0D;
    private double length = 0D;
    private double durationSimulation = 0D;
    private static long startTimeParse = 0;
    private static long finishTimeParse = 0;
    public static ArrayList<WiObject> _lstNodesOrg;
    public static ArrayList<WiObject> _lstEventsOrg;
    public static ArrayList<WiObject> _lstPacketsOrg;
    private static ArrayList<WiObject> _lstNodes;
    private static ArrayList<WiObject> _lstEvents;
    private static ArrayList<WiObject> _lstPackets;
    private IFilterDataCollector _filter = null;
    private boolean isParsed = false;

    /* Constructor */
    public WiModelManager() {
	this("", "");
    }

    public WiModelManager(String fileNameTrace) {
	this(fileNameTrace, "");
    }

    public WiModelManager(String fileNameTrace, String fileNameConfig) {
	if (!fileNameConfig.equals("")) {
	    _fileNameConfig = fileNameConfig;
	}
	if (!fileNameTrace.equals("")) {
	    _fileNameTrace = fileNameTrace;
	}

	init();
    }

    public void init() {
	_lstNodesOrg = new ArrayList<>();
	_lstEventsOrg = new ArrayList<>();
	_lstPacketsOrg = new ArrayList<>();
	_lstNodes = new ArrayList<>();
	_lstEvents = new ArrayList<>();
	_lstPackets = new ArrayList<>();

//	if (_lstNodesOrg == null) {
//	    _lstNodesOrg = new ArrayList<>();
//	}
//	if (_lstEventsOrg == null) {
//	    _lstEventsOrg = new ArrayList<>();
//	}
//	if (_lstPacketsOrg == null) {
//	    _lstPacketsOrg = new ArrayList<>();
//	}
//
//	if (_lstNodes == null) {
//	    _lstNodes = new ArrayList<>();
//	}
//	if (_lstEvents == null) {
//	    _lstEvents = new ArrayList<>();
//	}
//	if (_lstPackets == null) {
//	    _lstPackets = new ArrayList<>();
//	}
    }

    public static WiModelManager getInstance() {
	if (_mng == null) {
	    _mng = new WiModelManager();
	}
	return _mng;
    }

    public static void clearData() {
	WiModelManager.getInstance().init();
    }

    public void setFilterListener(IFilterDataCollector listener) {
	this._filter = listener;
    }

    public void reset() {
	_lstNodes = new ArrayList<>(_lstNodesOrg);
	_lstEvents = new ArrayList<>(_lstEventsOrg);
	_lstPackets = new ArrayList<>(_lstPacketsOrg);
    }
    /* Parse Manager */

    public void parse() throws WiException {
	try {
	    if (_fileNameTrace.equals("")) {
		setParsed(false);
		throw new WiException(WiException.TYPE_DEFAULT, "file trace invalid!");
	    }
	    WiModelParser parser = new WiModelParser(_fileNameTrace);
	    parser.parse();
	    setParsed(true);
	} catch (WiException e) {
	    _mng.setParsed(false);
	    throw new WiException(WiException.TYPE_PARSE, e.getMessage());

	}
    }

    public void parse(boolean debug, boolean useNative) throws WiException {
	System.gc();
	
	reset();
	
	setStartTimeParse(System.currentTimeMillis());
	if (useNative) {

	    try {
		if (_fileNameTrace.equals("")) {
		    setParsed(false);
		    throw new WiException(WiException.TYPE_DEFAULT, "file trace invalid!");
		}
		HelloWorld helloParse = new HelloWorld(this);
		if (debug) {
		    helloParse.setDebug(1);
		} else {
		    helloParse.setDebug(0);
		}
		helloParse.parse(_fileNameTrace);
		setParsed(true);
	    } catch (WiException e) {
		_mng.setParsed(false);
		throw new WiException(WiException.TYPE_PARSE, e.getMessage());

	    }
	} else {
	    parse();
	}
	setFinishTimeParse(System.currentTimeMillis());

    }

    public void cancelParse() {
    }

    public boolean isParsed() {
	return isParsed;
    }

    public void setParsed(boolean isParsed) {
	this.isParsed = isParsed;
    }

    /* Setting */
    public void setTraceFile(String fileNameTrace) {
	this._fileNameTrace = fileNameTrace;
    }

    public void setConfigFile(String fileNameConfig) {
	this._fileNameConfig = fileNameConfig;
    }
    /* =======Add WiObject to list======== */

    public int addWiObjectOrg(WiObject object) throws WiException {
	if (object == null) {
	    throw new WiException(WiException.TYPE_NULL, "Can not add Object!");
	}

	if (object instanceof Node) {
	    return addNodeOrg((Node) object);
	} else if (object instanceof Event) {
	    return addEventOrg((Event) object);
	} else if (object instanceof Packet) {
	    return addPacketOrg((Packet) object);
	} else {
	    throw new WiException(WiException.TYPE_MODEL_EX, "Type object invalid!");
	}
    }

    public int addNodeOrg(Node node) {
	_lstNodesOrg.add(node);
	if(FLAG_DEBUG) return -1;
	if (_filter != null) {
	    _filter.addNode(node);
	}
	return _lstNodesOrg.indexOf(node);
    }

    public int addNode(Node node) {
	_lstNodes.add(node);
	if (_filter != null) {
	    _filter.addNode(node);
	}
	return _lstNodes.indexOf(node);
    }

    public int addEventOrg(Event event) {
	_lstEventsOrg.add(event);
	if(FLAG_DEBUG) return -1;
	if (_filter != null) {
	    _filter.addEvent(event);
	}
	return _lstEventsOrg.indexOf(event);
    }

    public int addEvent(Event event) {
	_lstEvents.add(event);
	if (_filter != null) {
	    _filter.addEvent(event);
	}
	return _lstEvents.indexOf(event);
    }

    public int addPacketOrg(Packet packet) {
	_lstPacketsOrg.add(packet);
	if(FLAG_DEBUG) return -1;
	if (_filter != null) {
	    _filter.addPacket(packet);
	}
	return _lstPacketsOrg.indexOf(packet);
    }

    public int addPacket(Packet packet) {
	_lstPackets.add(packet);
	if (_filter != null) {
	    _filter.addPacket(packet);
	}
	return _lstPackets.indexOf(packet);
    }

    /* ===========Get WiObject By index=========== */
    public Node getNode(int index) {
	for(WiObject obj: _lstNodes){
	    Node node=(Node)obj;
	    if(node.id == index){
		return node;
	    }
	}
	return null;
    }

    public Event getEvent(int index) {
	return (Event) _lstEvents.get(index);
    }

    public Packet getPacket(int index) {
	return (Packet) _lstPackets.get(index);
    }

    public Node getNodeOrg(int index) {
	return (Node) _lstNodesOrg.get(index);
    }

    public Event getEventOrg(int index) {
	return (Event) _lstEventsOrg.get(index);
    }

    public Packet getPacketOrg(int index) {
	return (Packet) _lstPacketsOrg.get(index);
    }
    /* ===========Get list WiObject========= */

    public ArrayList<WiObject> getListNodes() {
	return _lstNodes;
    }

    public ArrayList<WiObject> getListEvents() {
	return _lstEvents;
    }

    public ArrayList<WiObject> getListPackets() {
	return _lstPackets;
    }
    /* ===========Get-Set Other==================*/

    public String getNameFileTrace() {
	return _fileNameTrace;
    }

    public String getNameFileConfig() {
	return _fileNameConfig;
    }

    public static long getStartTimeParse() {
	return startTimeParse;
    }

    public static void setStartTimeParse(long startTimeParse) {
	WiModelManager.startTimeParse = startTimeParse;
    }

    public static long getFinishTimeParse() {
	return finishTimeParse;
    }

    public static void setFinishTimeParse(long finishTimeParse) {
	WiModelManager.finishTimeParse = finishTimeParse;
    }

    public double getDurationSimulation() {
	return durationSimulation;
    }

    public double getLength() {
	return length;
    }

    public double getWidth() {
	return width;
    }

    public void setDurationSimulation(double durationSimulation) {
	this.durationSimulation = durationSimulation;
    }

    public void setLength(double length) {
	this.length = length;
    }

    public void setWidth(double width) {
	this.width = width;
    }
}
